#!/cygdrive/c/Python27/python.exe
# -*- coding: utf-8 -*-

import os
import wx
import threading
import platform
from multiprocessing import Process
import zipfile
import hashlib

from AnalysisXML.AXML import AXML
from CheckProtect import CheckProtect
from AnalysisDEX.InitDEX import InitDEX
from ApkInfoForm import ApkInfoForm
from AnalysisCSN.CSN import CSN


def Doinprocess(cmd):
    '''
    '''
    print cmd
    os.system(cmd)
    
# Define notification event for thread completion
EVT_RESULT_ID=wx.NewId()

def EVT_RESULT(win, func):
    """
    Define Result Event.
    """
    win.Connect(-1, -1, EVT_RESULT_ID, func)

#---------------------------------------------------------------------------

class CustomStatusBar(wx.StatusBar):
    def __init__(self, parent):
        wx.StatusBar.__init__(self, parent, -1)

        # This status bar has three fields
        self.SetFieldsCount(3)
        # Sets the three fields to be relative widths to each other.
        self.SetStatusWidths([-2, -1, -2])

        self.sizeChanged = False


    def StartProc(self):
        self.SetStatusText(u"处理中，请等待...", 2)
        #self.timer.Start(100)

    def StopProc(self):
        self.SetStatusText(u"处理完成", 2)


class ResultEvent(wx.PyEvent):
    """
    Simple event to carry arbitrary result data.
    """
    def __init__(self, data):
        """
        Init Result Event.
        """
        wx.PyEvent.__init__(self)
        self.SetEventType(EVT_RESULT_ID)
        self.data=data

class TestFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, u"Dex文件分析--wangweilicxy")
        panel = wx.Panel(self)

        icon = wx.Icon("res" + os.path.sep + "logo.png")
        self.SetIcon(icon)
        self.sb = CustomStatusBar(self)
        self.SetStatusBar(self.sb)

        self.unpackDir = ""

        nameLbl = wx.StaticText(panel, -1, u"文件：")
        self.name = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY);
        self.OpenBtn = wx.Button(panel, -1, u"打开")
        self.Bind(wx.EVT_BUTTON, self.OnClickOpenFile, self.OpenBtn)
        self.OpenBtn.SetDefault()

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        addrSizer = wx.GridBagSizer(hgap=1, vgap=5)
        addrSizer.Add(nameLbl, pos=(0,0), span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        addrSizer.Add(self.name, pos=(0,1), span=(1,3), flag=wx.EXPAND)
        addrSizer.Add(self.OpenBtn, pos=(0,4), span=(1,1), flag=wx.FIXED_MINSIZE)
        addrSizer.AddGrowableCol(3)
        #----------------apkinfo---------------------
        apkBox = wx.StaticBox(panel, -1, u"APK基本信息")
        apkBoxSizer = wx.StaticBoxSizer(apkBox, wx.VERTICAL)
        
        ApkinfoSizer = wx.GridBagSizer(hgap=5, vgap=9)
        apksize_lbl = wx.StaticText(panel, -1, u"文件大小：")
        apkverstr_lbl = wx.StaticText(panel, -1, u"版本：")
        self.apksize_txt = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY);
        self.apkverstr_txt = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY);
        ApkinfoSizer.Add(apksize_lbl, pos=(0,0), span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        ApkinfoSizer.Add(apkverstr_lbl, pos=(0,2), span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        ApkinfoSizer.Add(self.apksize_txt, pos=(0,1), span=(1,1), flag=wx.EXPAND)
        ApkinfoSizer.Add(self.apkverstr_txt, pos=(0,3), span=(1,1), flag=wx.EXPAND)

        apkname_lbl = wx.StaticText(panel, -1, u"文件包名：")
        apkvercode_lbl = wx.StaticText(panel, -1, u"版本号：")
        self.apkname_txt = wx.TextCtrl(panel, -1, "", size=(200,-1), style=wx.TE_READONLY);
        self.apkvercode_txt = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY);
        ApkinfoSizer.Add(apkname_lbl, pos=(1,0), span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        ApkinfoSizer.Add(apkvercode_lbl, pos=(1,2), span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        ApkinfoSizer.Add(self.apkname_txt, pos=(1,1), span=(1,1), flag=wx.EXPAND)
        ApkinfoSizer.Add(self.apkvercode_txt, pos=(1,3), span=(1,1), flag=wx.EXPAND)

        sysreq_lbl = wx.StaticText(panel, -1, u"系统需求：")
        serno_lbl = wx.StaticText(panel, -1, u"序列号：")
        self.sysreq_txt = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY);
        self.serno_txt = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY);
        ApkinfoSizer.Add(sysreq_lbl, pos=(2,0), span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        ApkinfoSizer.Add(serno_lbl, pos=(2,2), span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        ApkinfoSizer.Add(self.sysreq_txt, pos=(2,1), span=(1,1), flag=wx.EXPAND)
        ApkinfoSizer.Add(self.serno_txt, pos=(2,3), span=(1,1), flag=wx.EXPAND)

        actnum_lbl = wx.StaticText(panel, -1, u"Activity数量：")
        classnum_lbl = wx.StaticText(panel, -1, u"类数量：")
        self.actnum_txt = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY);
        self.classnum_txt = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY);
        ApkinfoSizer.Add(actnum_lbl, pos=(3,0), span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        ApkinfoSizer.Add(classnum_lbl, pos=(3,2), span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        ApkinfoSizer.Add(self.actnum_txt, pos=(3,1), span=(1,1), flag=wx.EXPAND)
        ApkinfoSizer.Add(self.classnum_txt, pos=(3,3), span=(1,1), flag=wx.EXPAND)

        apkmd5_lbl = wx.StaticText(panel, -1, u"APKMD5：")
        publisher_lbl = wx.StaticText(panel, -1, u"发行者：")
        issuer_lbl = wx.StaticText(panel, -1, u"签发人：")
        self.apkmd5_txt = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY);
        self.publisher_txt = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY);
        self.issuer_txt = wx.TextCtrl(panel, -1, "", style=wx.TE_READONLY);
        ApkinfoSizer.Add(apkmd5_lbl, pos=(4,0), span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        ApkinfoSizer.Add(publisher_lbl, pos=(5,0), span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        ApkinfoSizer.Add(issuer_lbl, pos=(6,0), span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
        ApkinfoSizer.Add(self.apkmd5_txt, pos=(4,1), span=(1,4), flag=wx.EXPAND)
        ApkinfoSizer.Add(self.publisher_txt, pos=(5,1), span=(1,4), flag=wx.EXPAND)
        ApkinfoSizer.Add(self.issuer_txt, pos=(6,1), span=(1,4), flag=wx.EXPAND)
        #----------------------------

        self.jiami_txt = wx.TextCtrl(panel, -1, "", size=(600,100), style=wx.TE_MULTILINE|wx.TE_READONLY);
        ApkinfoSizer.Add(self.jiami_txt, pos=(7,0), span=(2,4), flag=wx.EXPAND)
        #self.jiami_txt.Enable(False);

        cmdSizer = wx.GridBagSizer(hgap=1, vgap=4)
        self.DcmpBtn = wx.Button(panel, -1, u"反编译", size=(100,30))
        self.DcmpBtn.Enable(False)
        self.Bind(wx.EVT_BUTTON, self.OnClickDecompile, self.DcmpBtn)
        cmdSizer.Add(self.DcmpBtn, pos=(0,0), span=(1,1), flag=wx.EXPAND)

        self.CompBtn = wx.Button(panel, -1, u"编译并签名", size=(100,30))
        self.CompBtn.Enable(False)
        self.Bind(wx.EVT_BUTTON, self.OnClickCompile, self.CompBtn)
        cmdSizer.Add(self.CompBtn, pos=(0,1), span=(1,1), flag=wx.EXPAND)

        self.ShowAsjavaBtn = wx.Button(panel, -1, u"以java显示反编译结果", size=(100,30))
        self.ShowAsjavaBtn.Enable(False)
        self.Bind(wx.EVT_BUTTON, self.ShowAsjava, self.ShowAsjavaBtn)
        cmdSizer.Add(self.ShowAsjavaBtn, pos=(0,2), span=(1,1), flag=wx.EXPAND)

        self.ApkinfoBtn = wx.Button(panel, -1, u"Apk信息", size=(0,0))
        self.ApkinfoBtn.Enable(False)
        self.Bind(wx.EVT_BUTTON, self.ShowApkinfo, self.ApkinfoBtn)
        #cmdSizer.Add(self.ApkinfoBtn, pos=(0,2), span=(1,1), flag=wx.EXPAND)

        

        cmdSizer.AddGrowableCol(0)
        cmdSizer.AddGrowableCol(1)
        cmdSizer.AddGrowableCol(2)
        
        ApkinfoSizer.AddGrowableCol(1)
        ApkinfoSizer.AddGrowableCol(3)

        apkBoxSizer.Add(ApkinfoSizer, 0, wx.EXPAND|wx.ALL, 10)

        mainSizer.Add(addrSizer, 0, wx.EXPAND|wx.ALL, 10)
        mainSizer.Add(apkBoxSizer, 0, wx.EXPAND|wx.ALL, 10)
        mainSizer.Add(cmdSizer, 0, wx.EXPAND|wx.ALL, 10)

        panel.SetSizer(mainSizer)
        mainSizer.Fit(self)
        mainSizer.SetSizeHints(self)

        EVT_RESULT(self, self.UpdateDisplay)
        
    def UpdateDisplay(self, msg):
        """
        
        """
        self.sb.StopProc()
        self.OpenBtn.Enable(True)
        self.DcmpBtn.Enable(True)
        self.CompBtn.Enable(True)
        self.ApkinfoBtn.Enable(True)
        self.ShowAsjavaBtn.Enable(True)
        self.jiami_txt.SetValue(self.work.getProtectInfo())
        mfinfo = self.work.getManifestInfo()
        if mfinfo.get_filename_abs() == 'AndroidManifest':
            self.apkverstr_txt.SetValue(mfinfo.get_androidversion_code())
            self.apkname_txt.SetValue(mfinfo.get_package())
            self.apkvercode_txt.SetValue(mfinfo.get_androidversion_name())
            self.sysreq_txt.SetValue(mfinfo.getMinSdkVersion())
        csn = self.work.getCSN()
        self.apksize_txt.SetValue(str(csn.get_size()))
        self.serno_txt.SetValue(str(csn.getCertificateSN()).upper())
        self.apkmd5_txt.SetValue(str(self.work.getApkMD5()).upper())
        self.publisher_txt.SetValue(str(csn.getCertificateIDN()))
        self.issuer_txt.SetValue(str(csn.getCertificateSDN()))
        self.actnum_txt.SetValue(str(len(self.work.GetActivity())))
        self.classnum_txt.SetValue(str(self.work.GetClassNumber()))
        '''
        self.dexLbl_txt.SetValue(self.work.getDexHeaderInfo()["header_magic"])
        self.headLbl_txt.SetValue(self.work.getDexHeaderInfo()["header_headerSize"])
        self.byteLbl_txt.SetValue(self.work.getDexHeaderInfo()["header_endianTag"])

        self.sizeLbl_txt.SetValue(self.work.getDexHeaderInfo()["header_fileSize"])
        self.segoffLbl_txt.SetValue(self.work.getDexHeaderInfo()["header_linkOff"])
        self.segLbl_txt.SetValue(self.work.getDexHeaderInfo()["header_linkSize"])
        '''

    def ShowAsjava(self, event):
        """
        """
        decompile = self.unpackDir +  os.path.sep + "decompile"
        if not os.path.exists(decompile):
            wx.MessageDialog(None, u"请先进行反编译").ShowModal()
            return
        if platform.uname()[0] == 'Linux':
            wx.MessageDialog(None, u"此命令不支持linux").ShowModal()
            return
        else:
            cmd = 'tool\\jdgui\\jd-gui.exe %sd2j.jar' % (self.unpackDir + os.path.sep)
            p = Process(target=Doinprocess, args=(cmd,))
            p.start()
            p.join(5)
        
    def ShowApkinfo(self, event):
        """
        """
        ApkInfoForm(len(self.work.GetActivity())).Show()
        
    def OnClickDecompile(self, event):
        self.sb.StartProc()
        re = -9999
        if platform.uname()[0] == 'Linux':
            cmd = 'tool/apktool/apktool d -f -o%s %s'
            cmd2 = 'tool/dex2jar-2.0/d2j-dex2jar.sh %sclasses.dex -o %sd2j.jar'
        else:
            cmd = 'tool\\apktool\\apktool.bat d -f -o%s %s'
            cmd2 = 'tool\\dex2jar-2.0\\d2j-dex2jar.bat %sclasses.dex -o %sd2j.jar'
        if os.path.exists(self.apkpathname):
            re = os.system(cmd % (self.unpackDir +  os.path.sep + "decompile", self.apkpathname))
        if re == 0:            
            os.system(cmd2 % (self.unpackDir +  os.path.sep, self.unpackDir +  os.path.sep))
            #print cmd2 % (self.unpackDir +  os.path.sep, self.unpackDir +  os.path.sep)
        self.sb.StopProc()

    def OnClickCompile(self, event):
        apk = os.path.basename(self.apkpathname)
        out = self.unpackDir + os.path.sep + "out"
        sout = self.unpackDir + os.path.sep + "signed_out"
        decompile = self.unpackDir +  os.path.sep + "decompile"
        if not os.path.exists(decompile):
            wx.MessageDialog(None, u"请先进行反编译").ShowModal()
            return
        if not os.path.exists(sout):
            os.makedirs(sout) 
        if platform.uname()[0] == 'Linux':
            cmp_cmd = 'tool/apktool/apktool b ' + self.unpackDir +  os.path.sep + "decompile -o " + out + os.path.sep + apk
        else:
            cmp_cmd = 'tool\\apktool\\apktool.bat b ' + self.unpackDir +  os.path.sep + "decompile -o " + out + os.path.sep + apk
            print "cmp_cmd = " + cmp_cmd
            self.sb.SetStatusText(u"正在编译，请等待...", 2)
            os.system(cmp_cmd)
        if platform.uname()[0] == 'Linux':
            sig_cmd = 'java -jar tool/apksigner/signapk.jar tool/apksigner/platform.x509.pem tool/apksigner/platform.pk8 ' + out + os.path.sep + apk + " " + sout + os.path.sep + apk
        else:
            sig_cmd = 'java -jar tool\\apksigner\\signapk.jar tool\\apksigner\\testkey.x509.pem tool\\apksigner\\testkey.pk8 ' + out + os.path.sep + apk + " " + sout + os.path.sep + apk
            print "sig_cmd= " + sig_cmd
            self.sb.SetStatusText(u"正在签名，请等待...", 2)
            os.system(sig_cmd)
            self.sb.SetStatusText(u"处理完成", 2)

    def OnClickOpenFile(self, event):
        fd = wx.FileDialog(None, wildcard=("APK files (*.apk)|*.apk"))
        fd.ShowModal()
        self.name.Clear()
        self.apkname = fd.GetFilename()
        self.apkpathname = fd.GetPath() # + os.path.sep + fd.GetFilename()
        self.name.SetValue(self.apkpathname)
        if os.path.exists(self.apkpathname):
            self.unpackDir = os.path.expanduser('~') + os.path.sep + "APK" + os.path.sep + self.apkname
            print self.unpackDir
            if os.path.exists(self.unpackDir):
                if wx.MessageDialog(None, u"目录已存在，是否覆盖？").ShowModal() == wx.ID_OK:
                    self.work = Worker(self, self.unpackDir, self.apkpathname)
                    self.work.start()
                    self.sb.StartProc()
                    self.OpenBtn.Enable(False)
                else:
                    self.OpenBtn.Enable(True)
                    self.DcmpBtn.Enable(True)
                    self.CompBtn.Enable(True)
                    self.ApkinfoBtn.Enable(True)
                    self.ShowAsjavaBtn.Enable(True)
            else:
                self.work = Worker(self, self.unpackDir, self.apkpathname)
                self.work.start()
                self.sb.StartProc()
                self.OpenBtn.Enable(False)
        else:
            self.sb.SetStatusText(u"文件不存在", 2)
            
           
class Worker(threading.Thread):
    def __init__(self, wxObject, unpackDir, pathname):
        self.unpackDir = unpackDir
        self.pathname = pathname
        self.wxObject = wxObject
        self.activity = {}
        self.classnum = 0
        threading.Thread.__init__(self)
        
    def run(self):
        self.UnzipFile()
        self.DoAnalysis()
        wx.PostEvent(self.wxObject, ResultEvent("Thread finished!"))

    def UnzipFile(self):
        file = zipfile.ZipFile(self.pathname, "r")
        #self.unpackDir = os.path.expanduser('~') + os.path.sep + "APK" + os.path.sep + self.apkname
        file.extractall(self.unpackDir) #self.apkpathname.encode('GBK'))
        file.close()

    def DoAnalysis(self):
        '''
        分析加固，dumpdex，分析CSN
        '''
        # 分析加固，dumpdex
        obj = CheckProtect(self.unpackDir)
        self.protect_flag = obj.check_protectflag()
        self.activity = obj.getActivity()
        self.classnum = obj.getClassNum()
        # 分析dex
        #dexobj = InitDEX()
        #self.dexheader ={}
        #self.dexheader = dexobj.getDexInfo(self.unpackDir + os.path.sep + "classes.dex")
        # 分析CSN
        csn_path = self.unpackDir + os.path.sep + "META-INF"
        if os.path.isdir(csn_path):
            f_list = os.listdir(csn_path)
            for file_name in f_list:
                if os.path.splitext(file_name)[1] == '.RSA' or os.path.splitext(file_name)[1] == '.DSA':
                    csn_path = csn_path + os.path.sep + file_name
                    self.csn = CSN(csn_path)
                    '''
                    self.apkinfo.ui.edt_file.setText(str(csn.get_size()))
                    self.apkinfo.ui.edt_serial_num.setText(str(csn.getCertificateSN()).upper())
                    self.apkinfo.ui.edt_publisher.setText(str(csn.getCertificateIDN()))
                    self.apkinfo.ui.edt_issuer.setText(str(csn.getCertificateSDN()))
                    '''
                    break
        # 计算md5
        if self.pathname != "":
            self.apkmd5value = self.GetFileMd5(self.pathname)
        # 分析manifest
        path = self.unpackDir + os.path.sep +"AndroidManifest.xml"
        if os.path.exists(path):
            self.axml_analysis = AXML(self.unpackDir + os.path.sep +"AndroidManifest.xml")

    def GetClassNumber(self):
        return self.classnum
    
    def GetActivity(self):
        return self.activity
    
    def GetFileMd5(self, path):
        try:
            file = open(path, 'rb')
            md5 = hashlib.md5()
            strRead = ""
            while True:
                strRead = file.read(8096)
                if not strRead:
                    break
                md5.update(strRead)
            #read file finish
            strMd5 = md5.hexdigest()
            file.close()
            return strMd5
        except:
            return u"Sorry,this is something wrong !"
            
    def getProtectInfo(self):
        return self.protect_flag

    def getDexHeaderInfo(self):
        return self.dexheader

    def getCSN(self):
        return self.csn

    def getManifestInfo(self):
        return self.axml_analysis

    def getApkMD5(self):
        return self.apkmd5value


if __name__ == "__main__":
    app = wx.PySimpleApp()
    TestFrame().Show()
    app.MainLoop()
